package com.trade.dao.impl;

import com.trade.dao.*;
import com.trade.dao.util.TechnicalCalculationUtil;
import com.trade.data.*;
import com.trade.data.type.*;
import java.util.*;

import org.hibernate.*;

public class EquityDAO extends BaseDAO implements IEquityDAO {

	Session session = null;

	private ILivePriceDAO livePriceDAO;

	private Map<String, List<HistoricalDataVO>> equityHistoricalDataMap = new Hashtable<String, List<HistoricalDataVO>>();
	private Map<String, List<HistoricalDataVO>> equityIntradayHistoricalDataMap = new Hashtable<String, List<HistoricalDataVO>>();

	// private Map<String, Map<Integer, HistoricalDataVO>>
	// equitySMAHistoricalDataMap = new Hashtable<String, Map<Integer,
	// HistoricalDataVO>>();
	private Map<String, Map<Integer, HistoricalDataVO>> equitySMAIntradayHistoricalDataMap = new Hashtable<String, Map<Integer, HistoricalDataVO>>();

	// private Map<String, Map<Integer, HistoricalDataVO>>
	// equityEMAHistoricalDataMap = new Hashtable<String, Map<Integer,
	// HistoricalDataVO>>();
	private Map<String, Map<Integer, HistoricalDataVO>> equityEMAIntradayHistoricalDataMap = new Hashtable<String, Map<Integer, HistoricalDataVO>>();

	private Map<String, List<Double>> equityRSIIntradayHistoricalDataMap = new Hashtable<String, List<Double>>();
	private Map<String, List<Double>> equityGainIntradayHistoricalDataMap = new Hashtable<String, List<Double>>();
	private Map<String, List<Double>> equityLossIntradayHistoricalDataMap = new Hashtable<String, List<Double>>();
	private Map<String, List<Double>> equityMACDLineIntradayHistoricalDataMap = new Hashtable<String, List<Double>>();
	private Map<String, List<Double>> equityMACDSignalLineIntradayHistoricalDataMap = new Hashtable<String, List<Double>>();

	public void setLivePriceDAO(ILivePriceDAO livePriceDAO) {
		this.livePriceDAO = livePriceDAO;
	}

	public Map<String, List<HistoricalDataVO>> getEquityHistoricalData() {
		return equityHistoricalDataMap;
	}

	public void setEquityHistoricalData(
			Map<String, List<HistoricalDataVO>> equityHistoricalData) {
		this.equityHistoricalDataMap = equityHistoricalData;
	}

	public Map<String, List<HistoricalDataVO>> getIntradayDataInCache() {
		return equityIntradayHistoricalDataMap;
	}

	/**
	 * This method add Intraday Historical data in cache for calculations
	 */
	public synchronized void addIntradayDataInCache(
			List<HistoricalDataVO> equityHistoricalDataList) throws Exception {

		Double averageGain;
		Double averageLoss;
		for (HistoricalDataVO equityHistoricalDataVO : equityHistoricalDataList) {

			String ticker = equityHistoricalDataVO.getSymbol();
			if ("NIFTY".equals(ticker)) {
				continue;
			}

			if (equityIntradayHistoricalDataMap != null) {
				List<HistoricalDataVO> historicalDataList = equityIntradayHistoricalDataMap
						.get(ticker);

				// Calculate RSI
				if (historicalDataList != null
						&& historicalDataList.size() == 13) {
					averageGain = TechnicalCalculationUtil
							.calculateAvgGainOrLossRSI(
									equityGainIntradayHistoricalDataMap,
									equityHistoricalDataVO);
					averageLoss = TechnicalCalculationUtil
							.calculateAvgGainOrLossRSI(
									equityLossIntradayHistoricalDataMap,
									equityHistoricalDataVO);

					System.out.println("Average Gain : " + averageGain);
					System.out.println("Average Loss : " + averageLoss);
				} else {
					if (equityGainIntradayHistoricalDataMap.containsKey(ticker)) {

						Double rsi = TechnicalCalculationUtil.calculateRSI(
								equityGainIntradayHistoricalDataMap,
								equityLossIntradayHistoricalDataMap,
								equityHistoricalDataVO);

						HistoricalDataVO tempVO = new HistoricalDataVO();
						tempVO.setSymbol(ticker);
						tempVO.setCalValue(rsi);
						addEquityRSIIntradayHistoricalData(tempVO);

						System.out.println("Equity RSI : " + rsi);
					}
				}
				// Calculate RSI End

				if (historicalDataList != null) {
					historicalDataList.add(equityHistoricalDataVO);
				} else {
					historicalDataList = new ArrayList<HistoricalDataVO>();
					historicalDataList.add(equityHistoricalDataVO);
					equityIntradayHistoricalDataMap.put(ticker,
							historicalDataList);
				}
			}

			// EMA Calculations
			double previousEMA = 0;

			for (MovingAveragePeriodTypes movingAverage : MovingAveragePeriodTypes
					.values()) {

				if (!equityEMAIntradayHistoricalDataMap
						.containsKey(equityHistoricalDataVO.getSymbol())
						|| (equityEMAIntradayHistoricalDataMap
								.get(equityHistoricalDataVO.getSymbol()) != null && !equityEMAIntradayHistoricalDataMap
								.get(equityHistoricalDataVO.getSymbol())
								.containsKey(movingAverage.getId()))

				) {
					Map<Integer, HistoricalDataVO> map = equitySMAIntradayHistoricalDataMap
							.get(equityHistoricalDataVO.getSymbol());
					if (map != null) {
						HistoricalDataVO histDataVO = (HistoricalDataVO) map
								.get(movingAverage.getId());
						previousEMA = histDataVO != null ? histDataVO
								.getCalValue() : 0;
					}
				} else {

					Map<Integer, HistoricalDataVO> map = equityEMAIntradayHistoricalDataMap
							.get(equityHistoricalDataVO.getSymbol());
					HistoricalDataVO histDataVO = (HistoricalDataVO) map
							.get(movingAverage.getId());
					previousEMA = histDataVO != null ? histDataVO.getCalValue()
							: 0;
				}

				Double ema = previousEMA != 0 ? TechnicalCalculationUtil
						.calculateEMA(equityHistoricalDataVO, movingAverage
								.getId(), previousEMA) : null;

				EquityHistoricalDataVO equityTemp = new EquityHistoricalDataVO();
				equityTemp.setCalDays(movingAverage.getId());
				equityTemp.setCalValue(ema);
				equityTemp.setSymbol(equityHistoricalDataVO.getSymbol());

				System.out.println(movingAverage.getId() + "EMA "
						+ equityTemp.getSymbol() + " : " + ema);

				if (ema != null && ema != 0) {

					Map<Integer, HistoricalDataVO> map = equityEMAIntradayHistoricalDataMap
							.get(equityTemp.getSymbol());
					if (map != null) {
						map.put(equityTemp.getCalDays(), equityTemp);
					} else {
						map = new HashMap<Integer, HistoricalDataVO>();
						map.put(equityTemp.getCalDays(), equityTemp);
						equityEMAIntradayHistoricalDataMap.put(equityTemp
								.getSymbol(), map);
					}
				}
			}

			//Calculate MACD Line
			
			Double macdLine = TechnicalCalculationUtil.calculateMACD(
					equityEMAIntradayHistoricalDataMap, equityHistoricalDataVO);
			
			Double ema9 = null ;
			HistoricalDataVO pHistoricalDataVO = new HistoricalDataVO();
			pHistoricalDataVO.setSymbol(equityHistoricalDataVO.getSymbol());
			pHistoricalDataVO.setCalValue(macdLine); 
			pHistoricalDataVO.setClose(macdLine);
			addEquityMACDLineIntradayHistoricalData(pHistoricalDataVO);
			
			List<Double> tempList = null;

			if (equityMACDLineIntradayHistoricalDataMap.containsKey(equityHistoricalDataVO
					.getSymbol())) {
				tempList = (List<Double>) equityMACDLineIntradayHistoricalDataMap
						.get(equityHistoricalDataVO.getSymbol());
				
				if( tempList.size() == 9){
					
					Double averageMacdLine = TechnicalCalculationUtil.calculateReverseAvgInList( equityMACDLineIntradayHistoricalDataMap , equityHistoricalDataVO , 9);
					ema9 = TechnicalCalculationUtil.calculateEMA(pHistoricalDataVO, 9, averageMacdLine);					
				}else if( tempList.size() > 9 ){
					
					if( equityMACDSignalLineIntradayHistoricalDataMap.containsKey(equityHistoricalDataVO.getSymbol())){
						List<Double> macdSignalLineList = equityMACDSignalLineIntradayHistoricalDataMap.get(equityHistoricalDataVO.getSymbol());
						
						Double averageMacdLine = macdSignalLineList != null && macdSignalLineList.size() > 0 ? macdSignalLineList.get(macdSignalLineList.size() - 1) : 0 ;
						ema9 = TechnicalCalculationUtil.calculateEMA(pHistoricalDataVO, 9, averageMacdLine);
					}else{
						
					}
				}
				
				if( ema9 != null){
					HistoricalDataVO signalHistoricalDataVO = new HistoricalDataVO();
					signalHistoricalDataVO.setSymbol(equityHistoricalDataVO.getSymbol());
					signalHistoricalDataVO.setCalValue(ema9);						
					addEquityMACDSignalLineIntradayHistoricalData(signalHistoricalDataVO);
				}
				
			}			
			
			if( ema9 != null){
				System.out.println("Equity MACD Line : "
						+ macdLine
						+ " , MACD Signal "+ ( ema9 )
						+ " , MACD Histogram "+ ( macdLine - ema9));
			}
			//END of MACD Calculation			

		}
	}

	/**
	 * To add SMA data in cache for symbol
	 * 
	 * @param historicalDataVO
	 */
	public synchronized void addEquitySMAIntradayHistoricalData(
			HistoricalDataVO historicalDataVO) {
		Map<Integer, HistoricalDataVO> map = null;
		if (equitySMAIntradayHistoricalDataMap.containsKey(historicalDataVO
				.getSymbol())) {
			map = equitySMAIntradayHistoricalDataMap.get(historicalDataVO
					.getSymbol());
			if (map != null) {
				map.put(historicalDataVO.getCalDays(), historicalDataVO);
			} else {
				map = new HashMap<Integer, HistoricalDataVO>();
				map.put(historicalDataVO.getCalDays(), historicalDataVO);
			}
		} else {
			map = new HashMap<Integer, HistoricalDataVO>();
			map.put(historicalDataVO.getCalDays(), historicalDataVO);
			equitySMAIntradayHistoricalDataMap.put(
					historicalDataVO.getSymbol(), map);
		}
	}

	/**
	 * Add Equity RSI in Map
	 * 
	 * @param historicalDataVO
	 */
	public synchronized void addEquityRSIIntradayHistoricalData(
			HistoricalDataVO historicalDataVO) {
		List<Double> gainOrLossList = null;

		if (equityRSIIntradayHistoricalDataMap.containsKey(historicalDataVO
				.getSymbol())) {
			gainOrLossList = (List<Double>) equityRSIIntradayHistoricalDataMap
					.get(historicalDataVO.getSymbol());
			if (gainOrLossList != null) {
				gainOrLossList.add(historicalDataVO.getCalValue());
			} else {
				gainOrLossList = new LinkedList<Double>();
				gainOrLossList.add(historicalDataVO.getCalValue());
			}
		} else {
			gainOrLossList = new LinkedList<Double>();
			gainOrLossList.add(historicalDataVO.getCalValue());
			equityRSIIntradayHistoricalDataMap.put(
					historicalDataVO.getSymbol(), gainOrLossList);
		}
	}

	/**
	 * Add Gain in Map
	 * 
	 * @param historicalDataVO
	 */

	public synchronized void addEquityGainIntradayHistoricalData(
			HistoricalDataVO historicalDataVO) {
		List<Double> gainOrLossList = null;

		if (equityGainIntradayHistoricalDataMap.containsKey(historicalDataVO
				.getSymbol())) {
			gainOrLossList = (List<Double>) equityGainIntradayHistoricalDataMap
					.get(historicalDataVO.getSymbol());
			if (gainOrLossList != null) {
				gainOrLossList.add(historicalDataVO.getCalValue());
			} else {
				gainOrLossList = new LinkedList<Double>();
				gainOrLossList.add(historicalDataVO.getCalValue());
			}
		} else {
			gainOrLossList = new LinkedList<Double>();
			gainOrLossList.add(historicalDataVO.getCalValue());
			equityGainIntradayHistoricalDataMap.put(historicalDataVO
					.getSymbol(), gainOrLossList);
		}
	}

	/**
	 * Add Loss in Equity Map
	 * 
	 * @param historicalDataVO
	 */
	public synchronized void addEquityLossIntradayHistoricalData(
			HistoricalDataVO historicalDataVO) {
		List<Double> gainOrLossList = null;

		if (equityLossIntradayHistoricalDataMap.containsKey(historicalDataVO
				.getSymbol())) {
			gainOrLossList = (List<Double>) equityLossIntradayHistoricalDataMap
					.get(historicalDataVO.getSymbol());
			if (gainOrLossList != null) {
				gainOrLossList.add(historicalDataVO.getCalValue());
			} else {
				gainOrLossList = new LinkedList<Double>();
				gainOrLossList.add(historicalDataVO.getCalValue());
			}
		} else {
			gainOrLossList = new LinkedList<Double>();
			gainOrLossList.add(historicalDataVO.getCalValue());
			equityLossIntradayHistoricalDataMap.put(historicalDataVO
					.getSymbol(), gainOrLossList);
		}
	}
	
	
	
	/**
	 * Add Equity MACD Line in Map
	 * 
	 * @param historicalDataVO
	 */
	public synchronized void addEquityMACDLineIntradayHistoricalData(
			HistoricalDataVO historicalDataVO) {
		List<Double> gainOrLossList = null;

		if (equityMACDLineIntradayHistoricalDataMap.containsKey(historicalDataVO
				.getSymbol())) {
			gainOrLossList = (List<Double>) equityMACDLineIntradayHistoricalDataMap
					.get(historicalDataVO.getSymbol());
			if (gainOrLossList != null) {
				gainOrLossList.add(historicalDataVO.getCalValue());
			} else {
				gainOrLossList = new LinkedList<Double>();
				gainOrLossList.add(historicalDataVO.getCalValue());
			}
		} else {
			gainOrLossList = new LinkedList<Double>();
			gainOrLossList.add(historicalDataVO.getCalValue());
			equityMACDLineIntradayHistoricalDataMap.put(
					historicalDataVO.getSymbol(), gainOrLossList);
		}
	}	
	
	public synchronized void addEquityMACDSignalLineIntradayHistoricalData(
			HistoricalDataVO historicalDataVO) {
		List<Double> gainOrLossList = null;

		if (equityMACDSignalLineIntradayHistoricalDataMap.containsKey(historicalDataVO
				.getSymbol())) {
			gainOrLossList = (List<Double>) equityMACDSignalLineIntradayHistoricalDataMap
					.get(historicalDataVO.getSymbol());
			if (gainOrLossList != null) {
				gainOrLossList.add(historicalDataVO.getCalValue());
			} else {
				gainOrLossList = new LinkedList<Double>();
				gainOrLossList.add(historicalDataVO.getCalValue());
			}
		} else {
			gainOrLossList = new LinkedList<Double>();
			gainOrLossList.add(historicalDataVO.getCalValue());
			equityMACDSignalLineIntradayHistoricalDataMap.put(
					historicalDataVO.getSymbol(), gainOrLossList);
		}
	}		

	public int addEquity(InstrumentVO intrumentVO) throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.save("InstrumentVO", intrumentVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.clear();
				session.close();
			}
		}

		return 0;
	}

	public int updateEquity(InstrumentVO intrumentVO) throws Exception {

		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.update("InstrumentVO", intrumentVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.clear();
				session.close();
			}
		}

		return 0;
	}

	public int deleteEquity(InstrumentVO intrumentVO) throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.delete("InstrumentVO", intrumentVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.clear();
				session.close();
			}
		}

		return 0;
	}

	public InstrumentVO getEquity(InstrumentVO intrumentVO) throws Exception {
		InstrumentVO instrumentVO = null;
		try {
			session = sessionFactory.openSession();
			Query query = session
					.createQuery("from InstrumentVO where symbol = :symbol");
			query.setParameter("symbol", intrumentVO.getSymbol());
			// query.setParameter("securityName",
			// intrumentVO.getSecurityName());
			instrumentVO = (InstrumentVO) query.uniqueResult();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.clear();
				session.close();
			}
		}

		return instrumentVO;
	}

	public List<InstrumentVO> getAllEquity(String filter) throws Exception {
		List<InstrumentVO> instrumentVOList = null;
		try {
			session = sessionFactory.openSession();
			Query query = session
					.createQuery("from InstrumentVO where symbol like '%:symbol%' or securityName like '%:securityName%'");
			query.setParameter("symbol", filter);
			query.setParameter("securityName", filter);
			instrumentVOList = (List<InstrumentVO>) query.list();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return instrumentVOList;
	}

	public int addHistoricalData(EquityHistoricalDataVO historicalDataVO)
			throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.save(historicalDataVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return 0;
	}

	public int updateHistoricalData(EquityHistoricalDataVO historicalDataVO)
			throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.update(historicalDataVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return 0;
	}

	public int deleteHistoricalData(EquityHistoricalDataVO historicalDataVO)
			throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.delete(historicalDataVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return 0;
	}

	public List<EquityHistoricalDataVO> getHistoricalData(
			HistoricalDataFilterVO historicalDataVO) throws Exception {
		List<EquityHistoricalDataVO> historicalDataVOList = null;
		try {
			session = sessionFactory.openSession();
			Query query = session
					.createQuery("from EquityHistoricalDataVO where date between :fromDate and :toDate");
			query.setParameter("fromDate", historicalDataVO.getFromDate());
			query.setParameter("toDate", historicalDataVO.getToDate());
			historicalDataVOList = (List<EquityHistoricalDataVO>) query.list();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return historicalDataVOList;
	}

	public int addIntradayData(EquityIntradayDataVO historicalDataVO)
			throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.save(historicalDataVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return 0;
	}

	public int updateIntradayData(EquityIntradayDataVO historicalDataVO)
			throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.update(historicalDataVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return 0;
	}

	public int deleteIntradayData(EquityIntradayDataVO historicalDataVO)
			throws Exception {
		try {
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.delete(historicalDataVO);
			tx.commit();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return 0;
	}

	public List<HistoricalDataVO> getIntradayData(
			HistoricalDataFilterVO historicalDataVO) throws Exception {
		List<HistoricalDataVO> historicalDataVOList = null;
		try {
			session = sessionFactory.openSession();
			Query query = session
					.createQuery("from EquityIntradayDataVO where date between :fromDate and :toDate");
			query.setParameter("fromDate", historicalDataVO.getFromDate());
			query.setParameter("toDate", historicalDataVO.getToDate());
			historicalDataVOList = (List<HistoricalDataVO>) query.list();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			if (session != null) {
				session.close();
			}
		}

		return historicalDataVOList;
	}
}